const fs = require('fs').promises
const path = require('path')
const crypto = require('crypto')
const OSS = require('ali-oss')
const COS = require('cos-nodejs-sdk-v5')
const qiniu = require('qiniu')
const ObsClient = require('esdk-obs-nodejs')
const { ApiError } = require('../utils/error')
const SystemConfig = require('../models/SystemConfig')
const logger = require('../utils/logger')

// 文件URL缓存
const urlCache = new Map()

class FileService {
  constructor() {
    this.localStoragePath = path.join(__dirname, '..', 'uploads')
    this.initLocalStorage()
  }

  /**
   * 初始化本地存储目录
   */
  async initLocalStorage() {
    try {
      // 确保所有必要的子目录都存在
      const subdirs = ['system', 'qrcodes', 'exports', 'imports', 'temp', 'avatars']
      for (const dir of subdirs) {
        const fullPath = path.join(this.localStoragePath, dir)
        try {
          await fs.mkdir(fullPath, { recursive: true })
        } catch (error) {
          if (error.code !== 'EEXIST') {
            logger.error(`创建目录 ${dir} 失败:`, error)
            throw error
          }
        }
      }
    } catch (error) {
      logger.error('初始化存储目录失败:', error)
      throw new Error('初始化存储目录失败')
    }
  }

  /**
   * 获取存储配置
   */
  async getStorageConfig() {
    const config = await SystemConfig.findByPk('storage')
    return config ? JSON.parse(config.value) : {
      type: 'local',
      provider: '',
      accessKey: '',
      secretKey: '',
      region: '',
      bucket: '',
      domain: '',          // 域名(必填)
      protocol: 'http'     // 协议(必填,下拉框选择,默认http)
    }
  }

  /**
   * 获取文件的完整URL
   */
  async getFileUrl(filename) {
    const config = await this.getStorageConfig()
    
    if (config.type === 'local') {
      return `/storage/${filename}`
    }

    // 域名处理:
    // 1. 去掉开头的 http:// 或 https://
    // 2. 去掉开头的 : 或 :// 或 //
    // 3. 去掉结尾的 /
    const domain = config.domain
      .replace(/^https?:\/\//, '')  // 去掉协议头
      .replace(/^(:\/\/|:|\/\/|\/)+/, '')  // 去掉开头的冒号和斜杠组合
      .replace(/\/+$/, '')          // 去掉结尾的斜杠

    // 直接返回完整URL，因为 filename 已经包含了必要的路径
    return `${config.protocol}://${domain}/${filename}`
  }

  /**
   * 计算文件的MD5
   */
  async calculateFileMD5(filepath) {
    const fileBuffer = await fs.readFile(filepath)
    return crypto.createHash('md5').update(fileBuffer).digest('hex')
  }

  /**
   * 检查文件是否需要上传
   */
  async shouldUploadFile(localPath, filename) {
    const config = await this.getStorageConfig()
    if (config.type === 'local') {
      return false
    }

    try {
      // 计算本地文件MD5
      const localMD5 = await this.calculateFileMD5(localPath)
      
      // 获取远程文件ETag
      let remoteETag = null
      switch (config.provider) {
        case 'aliyun':
          const client = new OSS({
            region: config.region,
            accessKeyId: config.accessKey,
            accessKeySecret: config.secretKey,
            bucket: config.bucket
          })
          const result = await client.head(filename)
          remoteETag = result.res.headers.etag
          break
        case 'tencent':
          const cos = new COS({
            SecretId: config.accessKey,
            SecretKey: config.secretKey
          })
          const cosResult = await new Promise((resolve, reject) => {
            cos.headObject({
              Bucket: config.bucket,
              Region: config.region,
              Key: filename
            }, (err, data) => {
              if (err) reject(err)
              else resolve(data)
            })
          })
          remoteETag = cosResult.ETag
          break
        case 'qiniu':
          const mac = new qiniu.auth.digest.Mac(config.accessKey, config.secretKey)
          const bucketManager = new qiniu.rs.BucketManager(mac)
          const qiniuResult = await new Promise((resolve, reject) => {
            bucketManager.stat(config.bucket, filename, (err, respBody, respInfo) => {
              if (err) reject(err)
              else resolve(respInfo.data)
            })
          })
          remoteETag = qiniuResult.hash
          break
        case 'huawei':
          const obsClient = new ObsClient({
            access_key_id: config.accessKey,
            secret_access_key: config.secretKey,
            server: `https://obs.${config.region}.myhuaweicloud.com`
          })
          const obsResult = await obsClient.getObjectMetadata({
            Bucket: config.bucket,
            Key: filename
          })
          remoteETag = obsResult.InterfaceResult.ETag
          break
      }

      // 移除ETag中的引号（如果有）
      remoteETag = remoteETag ? remoteETag.replace(/"/g, '') : null
      
      // 如果远程文件不存在或MD5不一致，需要上传
      return !remoteETag || remoteETag !== localMD5

    } catch (error) {
      logger.warn(`检查文件 ${filename} 是否存在时发生错误:`, error)
      // 如果获取远程文件信息失败，认为需要上传
      return true
    }
  }

  /**
   * 延迟切换文件URL
   */
  async switchToOssUrl(filename, localUrl) {
    // 先返回本地URL
    const cacheKey = `url:${filename}`
    urlCache.set(cacheKey, localUrl)

    const trySwitch = async (retryCount = 0) => {
      try {
        const config = await this.getStorageConfig()
        const ossUrl = `${config.protocol}://${config.domain}/uploads/${filename}`
        
        // 验证URL是否可访问
        const verifyUrl = `${config.protocol}://${config.domain}/uploads/${filename}`
        const response = await fetch(verifyUrl)
        if (response.ok) {
          // 更新缓存
          urlCache.set(cacheKey, ossUrl)
          
          // 更新数据库中的qr_code_path为完整URL
          const Certificate = require('../models/Certificate')
          await Certificate.update(
            { qr_code_path: ossUrl },
            { where: { qr_code_path: filename } }
          )
          
          logger.info(`文件 ${filename} URL已切换到对象存储`)
          return true
        }
        throw new Error('URL不可访问')
      } catch (error) {
        logger.warn(`切换文件 ${filename} URL失败 (第${retryCount + 1}次尝试):`, error)
        
        if (retryCount < 2) { // 最多重试2次
          const nextDelay = (retryCount + 2) * 2 * 60000 // 3分钟、5分钟
          setTimeout(() => trySwitch(retryCount + 1), nextDelay)
        } else {
          logger.error(`文件 ${filename} URL切换失败，将继续使用本地URL`)
        }
        return false
      }
    }

    // 1分钟后第一次尝试切换
    setTimeout(() => trySwitch(), 60000)

    return localUrl
  }

  /**
   * 获取文件的当前URL
   */
  async getCurrentUrl(filename) {
    const cacheKey = `url:${filename}`
    return urlCache.get(cacheKey) || await this.getFileUrl(filename)
  }

  /**
   * 保存文件（重写）
   */
  async saveFile(fileBuffer, filename) {
    // 如果文件名已经包含子目录，直接使用
    let subdir = 'temp'
    const parts = filename.split('/')
    if (parts.length > 1) {
      subdir = parts[0]
      filename = parts[parts.length - 1]
    } else if (filename.match(/^(logo|favicon|loginBackground|loginLogo)/)) {
      subdir = 'system'
    } else if (filename.endsWith('.png')) {
      subdir = 'qrcodes'
    }
    
    const targetDir = path.join(this.localStoragePath, subdir)
    const localPath = path.join(targetDir, filename)
    const ossPath = `${subdir}/${filename}`  // 用于对象存储的路径

    try {
      // 确保目标目录存在
      await fs.mkdir(targetDir, { recursive: true })
      
      // 1. 保存到本地
      await fs.writeFile(localPath, fileBuffer)
      const localUrl = `/uploads/${subdir}/${filename}`

      // 如果是本地存储或测试模式，直接返回本地URL
      const config = await this.getStorageConfig()
      if (config.type === 'local' || this.isTestMode(config)) {
        return localUrl
      }

      // 2. 检查是否需要上传到对象存储
      if (await this.shouldUploadFile(localPath, ossPath)) {
        try {
          // 同步上传到对象存储，使用带目录的路径
          await this.uploadToOss(config, fileBuffer, ossPath)
          logger.info(`文件 ${ossPath} 已成功上传到对象存储`)
          return this.getFileUrl(ossPath)
        } catch (error) {
          logger.error(`上传文件 ${ossPath} 到对象存储失败:`, error)
          // 如果上传失败，返回本地URL
          return localUrl
        }
      }

      return this.getFileUrl(ossPath)
    } catch (error) {
      logger.error(`保存文件 ${filename} 失败:`, error)
      throw new Error(`保存文件失败: ${error.message}`)
    }
  }

  /**
   * 上传到对象存储
   */
  async uploadToOss(config, fileBuffer, filename, retryCount = 3) {
    // 确保文件路径包含 uploads 前缀
    const ossPath = `uploads/${filename}`
    
    try {
      switch (config.provider) {
        case 'aliyun':
          return await this.saveToAliyun(config, fileBuffer, ossPath)
        case 'tencent':
          return await this.saveToTencent(config, fileBuffer, ossPath)
        case 'qiniu':
          return await this.saveToQiniu(config, fileBuffer, ossPath)
        case 'huawei':
          return await this.saveToHuawei(config, fileBuffer, ossPath)
        default:
          throw new ApiError(400, '不支持的服务商类型')
      }
    } catch (error) {
      if (retryCount > 0) {
        logger.warn(`上传文件 ${filename} 失败，${retryCount}秒后重试，剩余重试次数: ${retryCount-1}`)
        // 等待重试时间
        await new Promise(resolve => setTimeout(resolve, 1000))
        return this.uploadToOss(config, fileBuffer, filename, retryCount - 1)
      }
      throw error
    }
  }

  /**
   * 删除文件
   */
  async deleteFile(filename) {
    const config = await this.getStorageConfig()

    // 确保文件路径包含 uploads 前缀
    const ossPath = filename.startsWith('uploads/') ? filename : `uploads/${filename}`

    if (config.type === 'local') {
      // 删除本地文件
      const filepath = path.join(this.localStoragePath, filename)
      try {
        await fs.unlink(filepath)
      } catch (error) {
        if (error.code !== 'ENOENT') {
          throw error
        }
      }
      return
    }

    // 从对象存储删除
    try {
      switch (config.provider) {
        case 'aliyun':
          return await this.deleteFromAliyun(config, ossPath)
        case 'tencent':
          return await this.deleteFromTencent(config, ossPath)
        case 'qiniu':
          return await this.deleteFromQiniu(config, ossPath)
        case 'huawei':
          return await this.deleteFromHuawei(config, ossPath)
        default:
          throw new ApiError(400, '不支持的服务商类型')
      }
    } catch (error) {
      // 如果是文件不存在的错误，直接返回
      if (error.message.includes('no such file') || 
          error.message.includes('NoSuchKey') ||
          error.message.includes('not exist')) {
        logger.warn(`文件 ${filename} 在对象存储中不存在`)
        return
      }
      throw error
    }
  }

  /**
   * 迁移文件
   * 从一个存储位置迁移到另一个存储位置
   */
  async migrateFiles(sourceConfig, targetConfig) {
    // 获取源存储中的所有文件
    const files = await this.listFiles(sourceConfig)
    
    // 逐个迁移文件
    for (const file of files) {
      // 下载文件
      const fileBuffer = await this.downloadFile(sourceConfig, file.name)
      
      // 上传到新位置
      await this.saveFile(fileBuffer, file.name)
      
      // 从原位置删除
      await this.deleteFile(file.name)
    }
  }

  // 以下是各个云存储服务商的具体实现方法
  async saveToAliyun(config, fileBuffer, filename) {
    const client = new OSS({
      region: config.region,
      accessKeyId: config.accessKey,
      accessKeySecret: config.secretKey,
      bucket: config.bucket
    })
    
    await client.put(filename, fileBuffer)
    return this.getFileUrl(filename)
  }

  async saveToTencent(config, fileBuffer, filename) {
    const cos = new COS({
      SecretId: config.accessKey,
      SecretKey: config.secretKey
    })

    await new Promise((resolve, reject) => {
      cos.putObject({
        Bucket: config.bucket,
        Region: config.region,
        Key: filename,
        Body: fileBuffer
      }, (err, data) => {
        if (err) {
          reject(err)
        } else {
          resolve(data)
        }
      })
    })

    return this.getFileUrl(filename)
  }

  async saveToQiniu(config, fileBuffer, filename) {
    const mac = new qiniu.auth.digest.Mac(config.accessKey, config.secretKey)
    const putPolicy = new qiniu.rs.PutPolicy({
      scope: config.bucket
    })
    const uploadToken = putPolicy.uploadToken(mac)
    const formUploader = new qiniu.form_up.FormUploader()
    const putExtra = new qiniu.form_up.PutExtra()

    await new Promise((resolve, reject) => {
      formUploader.put(uploadToken, filename, fileBuffer, putExtra, (err, body, info) => {
        if (err) {
          reject(err)
        } else if (info.statusCode !== 200) {
          reject(new Error(body.error))
        } else {
          resolve(body)
        }
      })
    })

    return this.getFileUrl(filename)
  }

  async saveToHuawei(config, fileBuffer, filename) {
    const obsClient = new ObsClient({
      access_key_id: config.accessKey,
      secret_access_key: config.secretKey,
      server: `https://obs.${config.region}.myhuaweicloud.com`
    })

    await new Promise((resolve, reject) => {
      obsClient.putObject({
        Bucket: config.bucket,
        Key: filename,
        Body: fileBuffer
      }, (err, result) => {
        if (err) {
          reject(err)
        } else if (result.CommonMsg.Status !== 200) {
          reject(new Error(result.CommonMsg.Message))
        } else {
          resolve(result)
        }
      })
    })

    return this.getFileUrl(filename)
  }

  // 删除方法实现
  async deleteFromAliyun(config, filename) {
    const client = new OSS({
      region: config.region,
      accessKeyId: config.accessKey,
      accessKeySecret: config.secretKey,
      bucket: config.bucket
    })
    
    await client.delete(filename)
  }

  async deleteFromTencent(config, filename) {
    const cos = new COS({
      SecretId: config.accessKey,
      SecretKey: config.secretKey
    })

    await new Promise((resolve, reject) => {
      cos.deleteObject({
        Bucket: config.bucket,
        Region: config.region,
        Key: filename
      }, (err, data) => {
        if (err) {
          reject(err)
        } else {
          resolve(data)
        }
      })
    })
  }

  async deleteFromQiniu(config, filename) {
    const mac = new qiniu.auth.digest.Mac(config.accessKey, config.secretKey)
    const bucketManager = new qiniu.rs.BucketManager(mac)

    await new Promise((resolve, reject) => {
      bucketManager.delete(config.bucket, filename, (err, respBody, respInfo) => {
        if (err) {
          reject(err)
        } else if (respInfo.statusCode !== 200) {
          reject(new Error(respBody.error))
        } else {
          resolve(respBody)
        }
      })
    })
  }

  async deleteFromHuawei(config, filename) {
    const obsClient = new ObsClient({
      access_key_id: config.accessKey,
      secret_access_key: config.secretKey,
      server: `https://obs.${config.region}.myhuaweicloud.com`
    })

    await new Promise((resolve, reject) => {
      obsClient.deleteObject({
        Bucket: config.bucket,
        Key: filename
      }, (err, result) => {
        if (err) {
          reject(err)
        } else if (result.CommonMsg.Status !== 200) {
          reject(new Error(result.CommonMsg.Message))
        } else {
          resolve(result)
        }
      })
    })
  }

  /**
   * 列出存储中的所有文件
   */
  async listFiles(config) {
    if (config.type === 'local') {
      // 列出本地文件，包含 system、qrcodes 和 avatars 目录
      const results = []
      const subdirs = ['system', 'qrcodes', 'avatars']  // 添加 avatars 目录
      
      for (const dir of subdirs) {
        try {
          const dirPath = path.join(this.localStoragePath, dir)
          const files = await fs.readdir(dirPath)
          for (const name of files) {
            if (name !== '.gitkeep') {  // 排除 .gitkeep 文件
              results.push({
                name,
                url: `/uploads/${dir}/${name}`
              })
            }
          }
        } catch (error) {
          logger.warn(`读取目录 ${dir} 失败:`, error)
        }
      }
      
      return results
    }

    // 从对象存储列出文件
    switch (config.provider) {
      case 'aliyun':
        return await this.listFromAliyun(config)
      case 'tencent':
        return await this.listFromTencent(config)
      case 'qiniu':
        return await this.listFromQiniu(config)
      case 'huawei':
        return await this.listFromHuawei(config)
      default:
        throw new ApiError(400, '不支持的服务商类型')
    }
  }

  /**
   * 下载文件
   */
  async downloadFile(config, filename) {
    if (config.type === 'local') {
      // 从本地读取文件
      const filepath = path.join(this.localStoragePath, filename)
      return await fs.readFile(filepath)
    }

    // 从对象存储下载文件
    switch (config.provider) {
      case 'aliyun':
        return await this.downloadFromAliyun(config, filename)
      case 'tencent':
        return await this.downloadFromTencent(config, filename)
      case 'qiniu':
        return await this.downloadFromQiniu(config, filename)
      case 'huawei':
        return await this.downloadFromHuawei(config, filename)
      default:
        throw new ApiError(400, '不支持的服务商类型')
    }
  }

  // 列出文件的具体实现
  async listFromAliyun(config) {
    const client = new OSS({
      region: config.region,
      accessKeyId: config.accessKey,
      accessKeySecret: config.secretKey,
      bucket: config.bucket
    })
    
    const result = await client.list()
    return result.objects.map(obj => ({
      name: obj.name,
      url: this.getFileUrl(obj.name)
    }))
  }

  async listFromTencent(config) {
    const cos = new COS({
      SecretId: config.accessKey,
      SecretKey: config.secretKey
    })

    const result = await new Promise((resolve, reject) => {
      cos.getBucket({
        Bucket: config.bucket,
        Region: config.region
      }, (err, data) => {
        if (err) {
          reject(err)
        } else {
          resolve(data)
        }
      })
    })

    return result.Contents.map(item => ({
      name: item.Key,
      url: this.getFileUrl(item.Key)
    }))
  }

  async listFromQiniu(config) {
    const mac = new qiniu.auth.digest.Mac(config.accessKey, config.secretKey)
    const bucketManager = new qiniu.rs.BucketManager(mac)

    const result = await new Promise((resolve, reject) => {
      bucketManager.listPrefix(config.bucket, {
        limit: 1000
      }, (err, respBody, respInfo) => {
        if (err) {
          reject(err)
        } else if (respInfo.statusCode !== 200) {
          reject(new Error(respBody.error))
        } else {
          resolve(respBody)
        }
      })
    })

    return result.items.map(item => ({
      name: item.key,
      url: this.getFileUrl(item.key)
    }))
  }

  async listFromHuawei(config) {
    const obsClient = new ObsClient({
      access_key_id: config.accessKey,
      secret_access_key: config.secretKey,
      server: `https://obs.${config.region}.myhuaweicloud.com`
    })

    const result = await new Promise((resolve, reject) => {
      obsClient.listObjects({
        Bucket: config.bucket
      }, (err, result) => {
        if (err) {
          reject(err)
        } else if (result.CommonMsg.Status !== 200) {
          reject(new Error(result.CommonMsg.Message))
        } else {
          resolve(result)
        }
      })
    })

    return result.InterfaceResult.Contents.map(item => ({
      name: item.Key,
      url: this.getFileUrl(item.Key)
    }))
  }

  // 下载文件的具体实现
  async downloadFromAliyun(config, filename) {
    const client = new OSS({
      region: config.region,
      accessKeyId: config.accessKey,
      accessKeySecret: config.secretKey,
      bucket: config.bucket
    })
    
    const result = await client.get(filename)
    return Buffer.from(result.content)
  }

  async downloadFromTencent(config, filename) {
    const cos = new COS({
      SecretId: config.accessKey,
      SecretKey: config.secretKey
    })

    const result = await new Promise((resolve, reject) => {
      cos.getObject({
        Bucket: config.bucket,
        Region: config.region,
        Key: filename
      }, (err, data) => {
        if (err) {
          reject(err)
        } else {
          resolve(data)
        }
      })
    })

    return Buffer.from(result.Body)
  }

  async downloadFromQiniu(config, filename) {
    const mac = new qiniu.auth.digest.Mac(config.accessKey, config.secretKey)
    const bucketManager = new qiniu.rs.BucketManager(mac)
    
    // 使用统一的域名配置
    const deadline = Math.floor(Date.now() / 1000) + 3600 // 1小时有效期
    const privateDownloadUrl = bucketManager.privateDownloadUrl(
      `${config.protocol}://${config.domain}`,  // 使用统一的域名配置
      filename,
      deadline
    )

    // 下载文件
    const response = await fetch(privateDownloadUrl)
    if (!response.ok) {
      throw new Error(`下载失败: ${response.statusText}`)
    }
    return Buffer.from(await response.arrayBuffer())
  }

  async downloadFromHuawei(config, filename) {
    const obsClient = new ObsClient({
      access_key_id: config.accessKey,
      secret_access_key: config.secretKey,
      server: `https://obs.${config.region}.myhuaweicloud.com`
    })

    const result = await new Promise((resolve, reject) => {
      obsClient.getObject({
        Bucket: config.bucket,
        Key: filename
      }, (err, result) => {
        if (err) {
          reject(err)
        } else if (result.CommonMsg.Status !== 200) {
          reject(new Error(result.CommonMsg.Message))
        } else {
          resolve(result)
        }
      })
    })

    return Buffer.from(result.InterfaceResult.Content)
  }

  /**
   * 批量保存文件
   * @param {Array<{buffer: Buffer, filename: string}>} files - 文件数组
   * @returns {Promise<Array<{filename: string, url: string}>>}
   */
  async batchSaveFiles(files) {
    const results = []
    const startTime = Date.now()

    // 1. 先保存所有文件到本地
    for (const file of files) {
      const result = await this.saveFile(file.buffer, file.filename)
      results.push({
        filename: file.filename,
        url: result
      })
    }

    // 2. 记录批处理开始时间
    const batchId = `batch:${startTime}`
    urlCache.set(batchId, startTime)

    // 3. 1分钟后统一切换所有文件的URL
    setTimeout(async () => {
      try {
        const config = await this.getStorageConfig()
        if (config.type !== 'local') {
          for (const result of results) {
            const ossUrl = await this.getFileUrl(result.filename)
            const cacheKey = `url:${result.filename}`
            urlCache.set(cacheKey, ossUrl)
          }
          logger.info(`批次 ${batchId} 的文件URL已全部切换到对象存储`)
        }
      } catch (error) {
        logger.error(`批次 ${batchId} 切换URL失败:`, error)
      } finally {
        // 清理批次记录
        urlCache.delete(batchId)
      }
    }, 60000)

    return results
  }

  /**
   * 批量删除文件
   * @param {Array<string>} filenames - 文件名数组
   */
  async batchDeleteFiles(filenames) {
    const errors = []
    
    for (const filename of filenames) {
      try {
        await this.deleteFile(filename)
        // 清除URL缓存
        const cacheKey = `url:${filename}`
        urlCache.delete(cacheKey)
      } catch (error) {
        errors.push({
          filename,
          error: error.message
        })
        logger.error(`删除文件 ${filename} 失败:`, error)
      }
    }

    if (errors.length > 0) {
      throw new ApiError(500, '部分文件删除失败', { errors })
    }
  }

  /**
   * 测试存储连接
   * @param {Object} config - 存储配置
   */
  async testConnection(config = null) {
    try {
      if (!config) {
        config = await this.getStorageConfig()
      }

      // 对象存储必填项验证
      if (config.type !== 'local' && (!config.accessKey || !config.secretKey || !config.region || !config.bucket || !config.domain)) {
        throw new ApiError(400, '请填写完整的配置信息')
      }

      // 本地存储不需要测试
      if (config.type === 'local') {
        return { success: true, message: '本地存储无需测试连接' }
      }

      // 测试模式判断
      if (config.accessKey === 'test' && config.secretKey === 'test') {
        return {
          success: true,
          message: '测试模式：连接成功'
        }
      }

      // 测试对象存储连接
      switch (config.provider) {
        case 'aliyun':
          const client = new OSS({
            region: config.region,
            accessKeyId: config.accessKey,
            accessKeySecret: config.secretKey,
            bucket: config.bucket
          })
          await client.list({ 'max-keys': 1 })
          break

        case 'tencent':
          const cos = new COS({
            SecretId: config.accessKey,
            SecretKey: config.secretKey
          })
          await new Promise((resolve, reject) => {
            cos.headBucket({
              Bucket: config.bucket,
              Region: config.region
            }, (err) => {
              if (err) reject(new ApiError(400, '腾讯云存储连接失败'))
              else resolve()
            })
          })
          break

        case 'qiniu':
          const mac = new qiniu.auth.digest.Mac(config.accessKey, config.secretKey)
          const bucketManager = new qiniu.rs.BucketManager(mac)
          await new Promise((resolve, reject) => {
            bucketManager.getBucketInfo(config.bucket, (err, respBody, respInfo) => {
              if (err || respInfo.statusCode !== 200) reject(new ApiError(400, '七牛云存储连接失败'))
              else resolve()
            })
          })
          break

        case 'huawei':
          const obsClient = new ObsClient({
            access_key_id: config.accessKey,
            secret_access_key: config.secretKey,
            server: `https://obs.${config.region}.myhuaweicloud.com`
          })
          const result = await obsClient.getBucketMetadata({
            Bucket: config.bucket
          })
          if (result.CommonMsg.Status !== 200) {
            throw new ApiError(400, '华为云存储连接失败')
          }
          break

        default:
          throw new ApiError(400, '不支持的存储服务商')
      }

      return {
        success: true,
        message: '连接测试成功'
      }

    } catch (error) {
      // 如果是ApiError直接抛出，否则包装成ApiError
      if (error instanceof ApiError) {
        throw error
      }
      
      // 记录详细错误日志
      logger.error('存储连接测试失败:', error)
      
      // 抛出简化的错误信息
      throw new ApiError(400, `${config.provider === 'aliyun' ? '阿里云' : 
                               config.provider === 'tencent' ? '腾讯云' :
                               config.provider === 'qiniu' ? '七牛云' :
                               config.provider === 'huawei' ? '华为云' : '存储服务'}连接失败`)
    }
  }

  /**
   * 判断是否为测试模式
   */
  isTestMode(config) {
    return config.accessKey === 'test' && config.secretKey === 'test'
  }
}

module.exports = new FileService() 